mov
instruction. To send data to an output device,
the CPU simply moves that data to a special memory location (in the I/O
address space if I/O-mapped input/output [see "The
I/O Subsystem" on page 92] or to an address in the memory address
space if using memory-mapped I/O). To read data from an input device, the
CPU simply moves data from the address (I/O or memory) of that device into
the CPU. Other than there are usually more wait states associated with a
typical peripheral device than actual memory, the input or output operation
looks very similar to a memory read or write operation (see "Memory
Access and the System Clock" on page 93).Note that I/O ports can be read-only, write-only, or read/write. The
port in the figure above, for example, is a write-only port. Since the outputs
on the latch do not loop back to the CPU's data bus, the CPU cannot read
the data the latch contains. Both the address decode and write control lines
must be active for the latch to operate; when reading from the latch's address
the decode line is active, but the write control line is not.
The figure below shows how to create a read/write input/output port. The
data written to the output port loops back to a transparent latch. Whenever
the CPU reads the decoded address the read and decode lines are active and
this activates the lower latch. This places the data previously written
to the output port on the CPU's data bus, allowing the CPU to read that
data. A read-only (input) port is simply the lower half of this figure;
the system ignores any data written to an input port.
A perfect example of an output port is a parallel printer port. The CPU
typically writes an ASCII character to a byte-wide output port that connects
to the DB-25F connect on the back of the computer's case. A cable transmits
this data to a the printer where an input port (to the printer) receives
the data. A processor inside the printer typically converts this ASCII character
to a sequence of dots it prints on the paper.
Generally, a given peripheral device will use more than a single I/O port.
A typical PC parallel printer interface, for example, uses three ports:
a read/write port, an input port, and an output port. The read/write port
is the data port (it is read/write to allow the CPU to read the last ASCII
character it wrote to the printer port). The input port returns control
signals from the printer; these signals indicate whether the printer is
ready to accept another character, is off-line, is out of paper, etc. The
output port transmits control information to the printer such as whether
data is available to print.
To the programmer, the difference between I/O-mapped and memory-mapped input/output
operations is the instruction to use. For memory-mapped I/O, any instruction
that accesses memory can access a memory-mapped I/O port. On the x86, the
mov, add, sub, cmp, and, or,
and not
instructions
can read memory; the mov
and not
instructions
can write data to memory. I/O-mapped input/output uses special instructions
to access I/O ports. For example, the x86 CPUs use the get
and put
instructions, the Intel 80x86 family uses the in
and out
instructions. The 80x86 in
and out
instructions work just like the mov
instruction except they
place their address on the I/O address bus rather than the memory address
bus (See "The I/O Subsystem" on
page 92.).
Memory-mapped I/O subsystems and I/O-mapped subsystems both require the
CPU to move data between the peripheral device and main memory. For example,
to input a sequence of ten bytes from an input port and store these bytes
into memory the CPU must read each value and store it into memory. For very
high-speed I/O devices the CPU may be too slow when processing this data
a byte at a time. Such devices generally contain an interface to the CPU's
bus so it directly read and write memory. This is known as direct memory
access since the peripheral device accesses memory directly, without using
the CPU as an intermediary. This often allows the I/O operation to proceed
in parallel with other CPU operations, thereby increasing the overall speed
of the system. Note, however, that the CPU and DMA device cannot both use
the address and data busses at the same time. Therefore, concurrent processing
only occurs if the CPU has a cache and is executing code and accessing data
found in the cache (so the bus is free). Nevertheless, even if the CPU must
halt and wait for the DMA operation to complete, the I/O is still much faster
since many of the bus operations during I/O or memory-mapped input/output
consist of instruction fetches or I/O port accesses which are not present
during DMA operations.
ax
to the printer port:
0000: mov bx, [FFE2] 0003: and bx, 1 0006: cmp bx, 0 0009: je 0000 000C: mov [FFE0], ax . . . . . .The first instruction fetches the data at the status input port. The second instruction logically ands this value with one to clear bits one through fifteen and set bit zero to the current status of the printer port. Note that this produces the value zero in
bx
if the printer is busy,
it produces the value one in bx
if the printer is ready to
accept additional data. The third instruction checks bx
to
see if it contains zero (i.e., the printer is busy). If the printer is busy,
this program jumps back to location zero and repeats this process over and
over again until the printer status bit is one.0000: mov bx, [FFE6] 0003: and bx, 1 0006: cmp bx, 0 0009: je 0000 000C: mov ax, [FFE4] . . . . . .This type of I/O operation, where the CPU constantly tests a port to see if data is available, is polling, that is, the CPU polls (asks) the port if it has data available or if it is capable of accepting data. Polled I/O is inherently inefficient. Consider what happens in the previous code segment if the user takes ten seconds to press a key on the keyboard - the CPU spins in a loop doing nothing (other than testing the keyboard status port) for those ten seconds.
iret
(interrupt return) instruction handles
this task. An ISR should always end with this instruction so the ISR can
return control to the program it interrupted.